package com.atguigu.gmall.index.service;

import com.alibaba.fastjson.JSON;
import com.atguigu.gmall.common.bean.ResponseVo;
import com.atguigu.gmall.index.annotation.GmallCache;
import com.atguigu.gmall.index.feign.GmallPmsClient;
import com.atguigu.gmall.pms.entity.CategoryEntity;
import org.apache.commons.lang3.StringUtils;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import javax.annotation.PostConstruct;
import java.util.List;
import java.util.Random;
import java.util.concurrent.TimeUnit;

@Service
public class IndxeService {
    @Autowired
    private GmallPmsClient gmallPmsClient;

    @Autowired
    private StringRedisTemplate redisTemplate; //StringRedisTemplate序列化器综合性能较好

    @Autowired
    private RedissonClient redissonClient;

    private final static String KEY_PREFIX = "index:cates:";
    private final static String LOC_PREFIX="index:cates:lock";

    public List<CategoryEntity> queryLvl1Categories() {
        ResponseVo<List<CategoryEntity>> listResponseVo = gmallPmsClient.queryCategoriesById(0l);
        return listResponseVo.getData();
    }


    /**
     * 使用自定义注解
     * @param pid
     * @return
     */
    @GmallCache(prefix = KEY_PREFIX,timeout = 129600,random = 14400,lock = LOC_PREFIX)
    public List<CategoryEntity> queryLv123CatesByPid(Long pid) {
            ResponseVo<List<CategoryEntity>> listResponseVo = gmallPmsClient.queryLvl23CateriesByPid(pid);
            List<CategoryEntity> categoryEntities = listResponseVo.getData();
            return categoryEntities;
    }

    public List<CategoryEntity> queryLv123CatesByPid2(Long pid) {
        //1.先查缓存,如果缓存命中,则直接返回
        String json = redisTemplate.opsForValue().get(KEY_PREFIX + pid);
        if (StringUtils.isNotBlank(json)) {
            return JSON.parseArray(json, CategoryEntity.class);
        }

        //添加分布式锁 预防 缓存击穿问题   只需要锁当前pid对应的数据即可
        RLock fairLock = redissonClient.getFairLock(LOC_PREFIX + pid);
        fairLock.lock();

        try {
            //在请求排队获取锁的过程中,可能已经有其他请求把数据存入缓存,此时应该再去确认一下缓存数据
            String json2 = redisTemplate.opsForValue().get(KEY_PREFIX + pid);
            if (StringUtils.isNotBlank(json2)) {
                return JSON.parseArray(json2, CategoryEntity.class);
            }

            //2.如果缓存没有命中,直接远程调用查询分类,放入缓存
            ResponseVo<List<CategoryEntity>> listResponseVo = gmallPmsClient.queryLvl23CateriesByPid(pid);
            List<CategoryEntity> categoryEntities = listResponseVo.getData();

            //判断查询结果集是否为空,如果为空,缓存时间不能超过5MIN
            if (CollectionUtils.isEmpty(categoryEntities)) {
                //为了防止缓存穿透,数据即使为空也缓存,缓存时间不能超过5分钟
                redisTemplate.opsForValue().set((KEY_PREFIX + pid), JSON.toJSONString(categoryEntities), 5, TimeUnit.MINUTES);
            } else {
                //为了防止缓存雪崩,给缓存时间添加随机值
                redisTemplate.opsForValue().set((KEY_PREFIX + pid), JSON.toJSONString(categoryEntities), 90 + new Random().nextInt(10), TimeUnit.DAYS);

            }
            return categoryEntities;
        } finally { //在finally 确保一定执行解锁操作
            fairLock.unlock();
        }
    }
}
